package com.smrtcold.dev.task;

import com.alibaba.fastjson.JSONObject;
import com.smrtcold.dev.init.SystemInit;
import com.smrtcold.dev.dto.dev.base.DevData;
import com.smrtcold.dev.dto.dev.task.DevTask;
import com.smrtcold.dev.dto.dev.base.SisData;
import com.smrtcold.dev.dto.dev.base.XMZDevInfo;
import com.smrtcold.dev.mapper.DataMapper;
import com.smrtcold.dev.service.base.BaseServer;
import lombok.extern.log4j.Log4j2;
import org.quartz.Job;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

import java.time.Duration;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.stream.Collectors;


/**
 * 这段为恒伟定制化程序
 * 1.先读取机组的吸气压力，频率--->如果吸气压力小于0.4pa 并且当前运行的机组的评率>=50Hz 执行后续操作
 * 2.判断制冷中的风机数量>=4 执行后续操作
 * 3.如果当前温度无明显下降趋势，接近停机温度， 且超过了10分钟（阈值）--->每次读取判断是不是太复杂，在读取数据过工程是否可以实时记录？
 * 4. 如果-1 后大于风机保护值 执行风机-1 操作
 * <p>
 * <p>
 * 优化后逻辑：
 * 1.先判断当前温度是否接近停机温度
 * 2.再次执行以上逻辑----
 */
@Log4j2
@Service
public class FanJobExecution implements Job {


    @Value("${sys.apid}")
    private String apid;
    @Autowired
    private DataMapper dataMapper;
    @Autowired
    private BaseServer baseServer;


    private int mid=0;
    private Long taskid;
    private double referlowpressure;
    private double referlowpressuredifference;
    private Double lowtemp;//下限--开始减风机数量
    private Double uptemp;//上限--开始减风机数量

    private Double bufferZone;// // 温度缓冲阈值
    private int queryTime;
    private int controlInterval;  // 最小控制间隔（秒）
    private int diffhz;//频率范围

    private static LocalDateTime lastctlTime;//上次控制时间
    private static int runfanNum;//当前运行风机数量

    private double setStoptemp;//设定的停机温度
    private double setStarttemp;//设定的启动温度
    private double cutttemp;//当前温度
    private int minRunFanNum;//设定最小的风机数量'
    private int maxFanNum;//设定最大的风机数量'

    //    private Double bufferZone = 0.1; // 温度缓冲阈值
    private double slopeThreshold = 0.1; // 趋势变化阈值（℃/分钟）

    private int control=0;//是否控制



    @Override
    public void execute(JobExecutionContext context) throws JobExecutionException {
        StringBuffer logContert = new StringBuffer();
        List<DevData> temp = new ArrayList<>();
        DevTask task = (DevTask) context.getJobDetail().getJobDataMap().get("TASK");
        this.taskid = task.getId();
        this.mid = task.getMid().intValue();
        JSONObject jsonObject = JSONObject.parseObject(task.getParameters());
        this.bufferZone = jsonObject.getDoubleValue("bufferZone");
        this.referlowpressure = jsonObject.getDoubleValue("referlowpressure");
        this.referlowpressuredifference = jsonObject.getDoubleValue("referlowpressuredifference");
        this.lowtemp = jsonObject.getDoubleValue("lowtemp");
        this.uptemp = jsonObject.getDoubleValue("uptemp");
        this.queryTime = jsonObject.getIntValue("queryTime");
        this.controlInterval = jsonObject.getIntValue("controlInterval");//控制间隔
        this.diffhz = jsonObject.getIntValue("diffhz");//控制间隔
        int status = 0;
        this.control=0;
        try {
            this.run(logContert, temp);
            if (temp.size() > 0) {
                this.dataMapper.add(temp);
            }
            status = 1;
        } catch (Exception e) {
            status = 0;
            log.error("任务执行失败：{}", e.getMessage());
            logContert.append(e.getMessage());
            //            todo 推送异常日志
           // push(String.format("/%s/%s/log", apid, taskid), logContert.toString());
        }
        this.dataMapper.addlog(taskid, status,this.control,logContert.toString());

    }


    /**
     * 执行任务
     *
     * @param logContert
     * @param saveData
     * @throws Exception
     */
    private void run(StringBuffer logContert, List<DevData> saveData) throws Exception {
        //1.判断控制周期
        boolean islocak = lastctlTime != null && Duration.between(lastctlTime, LocalDateTime.now()).getSeconds() < controlInterval;
        boolean isnext = this.getParameters(logContert, saveData, islocak);
        //获取当前库温近5分钟温度 以及设置参数（当前温度,停止、启动温度，最小风机数量，最大运行风机数量），这里提前调用是为了保存温度数据
        if(!isnext)return;
        //==================================================================判断当前是否处于最优模式运行=======================================
        //2.计算出basetemp 基线温度-- 用来判断是否加减风机
        double diff = this.setStarttemp - this.setStoptemp, basetemp = this.setStarttemp - (diff / 2);//etdiff = this.setStoptemp - this.cutttemp, stdiff = setStarttemp - this.cutttemp - setStarttemp,
        //低于基线温度 --  -19.1~19.25
        double cuttdif = Math.abs(this.cutttemp - basetemp);
        if (cuttdif < this.bufferZone) {
            String res=String.format("温控缓冲中...基线：%s，偏差：%s,设定值：%s", basetemp, cuttdif, this.bufferZone);
            logContert.append(res);
            log.info(res);
            return;
        }
        //前置为了获取风机数量
         isnext = this.getRunFanData(logContert, saveData);
        if (this.cutttemp < basetemp) {
            //温度小于下限--且出于降温或者持平状态
            if (this.cutttemp<this.lowtemp) {
                //4. 获取风机运行数量--并判断当前风机是否大于设定值--如果大于执行下一步操作
                if (!isnext) return;
                //5.获取压缩机运行状态-----运行数量，设定最小频率，最大运行频率，如果当前频率大于设定最小频率（35HZ） 可以执行下一步
                isnext = this.getRunCompressorData(logContert, saveData);
                if (!isnext) return;
                //获取近5分钟库温平均值
                SisData avgTemp = this.dataMapper.avgTemp("1000002", "VD 80", this.queryTime);
                //说明温度打不下来，这里需不需要判断？--- Math.abs(avgTemp.getValue() - this.cutttemp) <= 0.15
                if (null != avgTemp && avgTemp.getCount() > 5 &&this.cutttemp <= avgTemp.getValue()) {
                  //  int targetNum=  this.cutttemp-this.setStoptemp< bufferZone ?this.minRunFanNum:this.runfanNum - 1;
                    this.setFanNum(this.runfanNum - 1, logContert, saveData);
                    return;
                }
            } else {
                //在基线-19.07~-19.15  --->缓冲区，不执行动作如果升温会走下面增加风机逻辑
                logContert.append("偏差较小，温度缓冲中....");
                log.info("偏差较小，温度缓冲中....");
//                SisData avgTemp = this.dataMapper.avgTemp("1000002", "VD 80", this.queryTime);
//                if(this.cutttemp <avgTemp.getValue() ){
//                    this.setFanNum(this.runfanNum - 1, logContert, saveData);
//                }
                return;
            }
        } else if (this.cutttemp > basetemp) {
            //大于基线温度 进行风机数量加++
            //===================================================================以下为恒温设逻辑==================================================
            //获取近 设定${3}分钟温度平均值
            SisData sisData = this.dataMapper.avgTemp("1000002", "VD 80", this.queryTime);
            if (null != sisData && sisData.getCount() > 5) {
                //说明升温了
                if (sisData.getValue() < this.cutttemp) {
                    //如果大于设定温度，全部启动，否则+1
                    int targetNum = this.cutttemp > this.setStarttemp ? maxFanNum : (this.runfanNum == 0 ? this.minRunFanNum : this.runfanNum + 1);
                    String res=String.format("升温阶段，风机：%s -> %s", this.runfanNum, targetNum);
                    log.info(res);
                    logContert.append(res);
                    targetNum = Math.max(minRunFanNum, Math.min(maxFanNum, targetNum));
                    // 5. 执行风机控制
                    this.setFanNum(targetNum, logContert, saveData);
                }
//                else if( this.cutttemp< this.uptemp&&this.runfanNum>this.minRunFanNum )  {
//                      //呈下降趋势，执行减风机
//                    int targetNum =this.runfanNum - 1;
//                    String res=String.format("降温，温度：{},平均：{}，  风机：%s -> %s", this.cutttemp,sisData.getValue(), this.runfanNum, targetNum);
//                    log.info(res);
//                    logContert.append(res);
//                    this.setFanNum(targetNum, logContert, saveData);
//                }
                else {
                    //降温中...进入下一个个回合
                    logContert.append("降温中...持续观察");
                    log.info("降温中...持续观察");
                }
            } else {
                log.info("当前温度和基线温度持平，不动作");
            }
            //==========================================================以下代码为验证代码，不参与控制========================================
            List<DevData> devData = this.dataMapper.get("1000002", "VD 80", this.queryTime);
            double slope = calculateSlope(devData);
            int targetNum;
            if (0 == slope) {
                targetNum = this.runfanNum + 1;
            } else {
                if (cutttemp > setStarttemp) {
                    // 高于启动温度：需要制冷
                    targetNum = calculateCoolingMode(slope);
                } else if (cutttemp < setStoptemp) {
                    // 低于停止温度：减少制冷
                    targetNum = calculateReductionMode(slope);
                } else {
                    // 温度安全区内：趋势预判
                    targetNum = calculateBufferZoneMode(slope);
                }
            }
            // 4. 边界保护
            targetNum = Math.max(minRunFanNum, Math.min(maxFanNum, targetNum));
            log.info("验证控制风机数量：{}", targetNum);
            logContert.append("验证控制数：" + targetNum);
//            // 5. 执行风机控制
//            if (targetNum != runfanNum) {
//                this.setFanNum(targetNum, logContert, saveData);
//            }
        }

    }

    /**
     * 获取库温设定参数
     *
     * @param logContert
     * @param saveData
     * @throws Exception
     */
    public boolean getParameters(StringBuffer logContert, List<DevData> saveData, boolean islock) {
        try {
            String devid = "1000002";
            XMZDevInfo xmzDevInfo = SystemInit.get(devid);
            //vD 80：当前温度    VD274:停止值  VD270 启动值
            List<Float> temp = xmzDevInfo.batch("VD", "VD 80", "VD 2036", "VD 2028");//室温 ，下限 ，上限，
            //设置参数
            this.cutttemp = temp.get(0).doubleValue();
            this.setStoptemp = temp.get(1).doubleValue();
            this.setStarttemp = temp.get(2).doubleValue();
            saveData.add(new DevData(devid, "VD 80", temp.get(0), new Date()));
            if (islock) {
                String res = String.format("控制时间未到,%s/%s，跳过",Duration.between(lastctlTime, LocalDateTime.now()).getSeconds(), controlInterval);
                log.info(res);
                return false;
            }
            //判断是否能控制
            int isctltmp = xmzDevInfo.readAddres("VB 227.2").intValue();
            if(0==isctltmp){
                log.info("未开智能控制！");
                return false;
            }

            List<Short> fanset = xmzDevInfo.batch("VW", "VW 2040", "VW 2042");//最小风机 ，最大风机数量77
            this.minRunFanNum = fanset.get(0).intValue();
            this.maxFanNum = fanset.get(1).intValue();
            String res = String.format("温控范围：%s~%s,当前温度：%s ,风机范围:%s ~ %s", setStarttemp, setStoptemp, cutttemp, minRunFanNum, maxFanNum);
            log.info(res);
            return true;
        } catch (Exception e) {
            throw new RuntimeException("getParameters 获取参数失败," + e.getMessage());
        }

    }


    /**
     * 获取风机运行q
     *
     * @param logContert
     * @param saveData
     * @return
     * @throws Exception
     */
    private boolean getRunFanData(StringBuffer logContert, List<DevData> saveData) {
        //获取PLC 链接
        try {
            String devid = "1000002";
            XMZDevInfo xmzDevInfo = SystemInit.get(devid);
            //---风机1~8 状态
            List<Integer> data = xmzDevInfo.batch("B", "VB 10.2", "VB 11.0", "VB 11.6", "VB 12.4", "VB 13.2", "VB 14.0", "VB 14.6", "VB 15.4");//VD80:当前室内温度 VD 274:停止温度(-19.6)
            if (null != data) {
                int runNum = data.stream()
                        .filter(d -> 1 == d)
                        .collect(Collectors.toList()).size();//获取运行的风机数量

                //判断风机数量是否符合要求
                this.runfanNum = runNum;
                boolean isnext = runNum > this.minRunFanNum;//判断当前运行风机数量大于设定的最小风机数量
                String res = String.format(" \r\n风机运行：%s--->：%s", runNum, isnext);
                logContert.append(res);
                log.info(res);
                return isnext;
            } else {
                log.warn("  getRunFanData:当前plc--{} 未获取到数据，请检查链接信息！", devid);
                return false;
            }
        } catch (Exception e) {
            e.printStackTrace();
            throw new RuntimeException("getRunFanData 获取风机运行数据失败");
        }
    }

    /**
     * 先读取机组的吸气压力，频率--->如果吸气压力小于0.4pa 并且当前运行的机组的评率>=50Hz 执行后续操作
     * 注意：频率有多个，需要找到运行的或者最高的那个
     *
     * @return
     */
    private boolean getRunCompressorData(StringBuffer logContert, List<DevData> saveData) {
        try {
            String devid = "1000001";
            XMZDevInfo xmzDevInfo = SystemInit.get(devid);
            //VD70 低压压力显示值  最小频率,最大频率  压缩机1~3 频率 VD1210 1214 1218
            List<Float> data = xmzDevInfo.batch("VD", "VD 70", "VD 708", "VD 712", "VD 1210", "VD 1214", "VD 1218");
            boolean isrun = false;
            if (null != data && data.size() == 6) {
                Float lowpressure = data.remove(0);//获取吸气压力,这里存在问题，需要修正4台运行时压力为0.5
                Float maxpl = data.remove(0);//获取设定最大频率--VD708
                Float minpl = data.remove(0);//获取设定最小频率--VD712：
                data.sort((a, b) -> b.compareTo(a));//降序排序--获取运行的频率
                Float cuttmaxpl = data.get(0);//获取运行的最大频率
                //总运行数量
                int runNum = data.stream()
                        .filter(d -> d > 0)
                        .collect(Collectors.toList()).size();//获取运压缩机数量
                //总频率
                float sumhz = data.stream()
                        .reduce(0.0f, Float::sum);
                isrun =runNum>2||sumhz>this.diffhz||  ( cuttmaxpl > minpl && Math.abs(lowpressure - referlowpressure) < this.referlowpressuredifference);//  referlowpressure：为参考值，来自配置文件，方便调整
                String res = String.format("\n机组运行数量：%s，吸气压力：%s，运行最大频率：%s，总频率：%s, 结果：%s ", runNum, lowpressure, cuttmaxpl,sumhz, isrun);
                logContert.append(res);
                log.info(res);
                return isrun;
            } else {
                log.warn("getRunCompressorData 当前plc--{} 未获取到数据，请检查链接信息！", devid);
                return false;
            }
        } catch (Exception e) {
            e.printStackTrace();
            throw new RuntimeException("getRunCompressorData 获取压缩机运行数据失败");
        }
    }


    // 制冷模式计算
    private int calculateCoolingMode(double slope) {
        // 基础增量：每超过0.5℃增加1台
        int baseAdd = (int) Math.ceil((cutttemp - setStarttemp) / 0.5);

        // 趋势增量：温度持续上升时额外增加
        int trendAdd = slope > 0 ? (int) (slope * 10) : 0;

        return runfanNum + Math.max(1, baseAdd + trendAdd); // 至少增加1台
    }

    // 减载模式计算
    private int calculateReductionMode(double slope) {
        // 基础减量：每低于0.5℃减少1台
        int baseReduce = (int) Math.ceil((setStoptemp - cutttemp) / 0.5);

        // 趋势减量：温度快速下降时额外减少
        int trendReduce = slope < 0 ? (int) (Math.abs(slope) * 10) : 0;

        return runfanNum - Math.max(1, baseReduce + trendReduce); // 至少减少1台
    }

    // 安全区模式计算
    private int calculateBufferZoneMode(double slope) {
        // 上缓冲带（接近启动温度）
        if (cutttemp > setStarttemp - bufferZone) {
            return (slope > slopeThreshold) ?
                    runfanNum + 1 : // 温度上升趋势明显
                    runfanNum;
        }
        // 下缓冲带（接近停止温度）
        else if (cutttemp < setStoptemp + bufferZone) {
            return (slope < -slopeThreshold) ?
                    runfanNum - 1 : // 温度下降趋势明显
                    runfanNum;
        }
        // 稳定区不调整
        return runfanNum;
    }


    private synchronized boolean setFanNum(int targetNum, StringBuffer logContert, List<DevData> saveData) throws Exception {
        if(targetNum == this.minRunFanNum)return false;
        String devid = "1000002";
        XMZDevInfo xmzDevInfo = SystemInit.get(devid);
        xmzDevInfo.writeByAddres("VW 2044", targetNum);
        Thread.sleep(2000);
        int number = xmzDevInfo.readAddres("VW 2044").intValue();
        boolean isSuccess = number == targetNum;
        String res = String.format("\r\n控制风机：%s--> %s，实际：%s,结果：%s ",runfanNum, targetNum,number, isSuccess ? "成功" : "失败");
        logContert.append(res);
        log.info(res);
     //   if (isSuccess) {
            this.lastctlTime = LocalDateTime.now();
            this.runfanNum = targetNum;
            this.control=1;
            this.dataMapper.addKey("ck");//控制
       // }
        //推送控制日志
        this.baseServer.push(String.format("/%s/task/log/%s/%s", apid,mid, taskid), logContert.toString());
        return isSuccess;
    }


    // 计算温度变化斜率（℃/分钟）
    private double calculateSlope(List<DevData> tempData) {
        if (tempData.size() < 2) return 0.0;

        double sumX = 0, sumY = 0, sumXY = 0, sumX2 = 0;
        long baseTime = tempData.get(0).getTime().getTime();

        for (int i = 0; i < tempData.size(); i++) {
            DevData point = tempData.get(i);
            double x = (point.getTime().getTime() - baseTime);
            double y = point.getValue();

            sumX += x;
            sumY += y;
            sumXY += x * y;
            sumX2 += x * x;
        }
        int n = tempData.size();
        return (n * sumXY - sumX * sumY) / (n * sumX2 - sumX * sumX);
    }


}
